package com.gitee.l0km.beanfilter.core;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Objects;

import com.gitee.l0km.aocache.Singleton;
import com.gitee.l0km.beanfilter.core.context.FilterContext;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Sets;

import static com.google.common.base.MoreObjects.firstNonNull;

/**
 * 实现多个{@link Activation}条件过滤
 * @author guyadong
 *
 */
public class ActivationConditions  {
	private static final Activation[] EMPTY_PREDICATE = new Activation[0];
	private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
	private static final String[] EMPTY_STRING_ARRAY = new String[0];
	private final Activation[] conditions;
	private final boolean and;

	public ActivationConditions(Activation[] conditions, boolean and) {
		this.conditions = conditions == null || conditions.length==0 ? EMPTY_PREDICATE : conditions;
		this.and = and;
	}
	public ActivationConditions(Class<?>[] activeOnClasses, String[] activeOnClassNames, boolean activeAnd) {
		this(asActiveConditions(activeOnClasses,activeOnClassNames),activeAnd);
	}

	/**
	 * 根据组合 {@link Activation}实例判断是否应该激活过滤器
	 * @param filter
	 * @param beanClass
	 */
	public boolean isActive(IFilter filter, Class<?> beanClass) {
		if(null != conditions && conditions.length>0) {
			FilterContext ctx = new FilterContext(filter,beanClass);
			if(and) {
				for(Activation act : conditions) {
					if(!act.active(ctx)) {
						return false;
					}
				}
				return true;
			}else {
				for(Activation act : conditions) {
					if(act.active(ctx)) {
						return true;
					}
				}
				return false;
			}
		}
		return true;
	}

	private static final Function<String, Class<?>> AS_CLASS =new Function<String, Class<?>>(){

		@Override
		public Class<?> apply(String input) {
			try {
				return Class.forName(input);
			} catch (Exception e) {
				return null;
			}
		}};
	private static final Function<Class<?>,Activation> AS_ACTIVATION = new  Function<Class<?>,Activation>(){
		@Override
		public Activation apply(Class<?> input) {
			try {
				return (Activation) Singleton.singletonOf(input);
			} catch (Exception e) {
				return null;
			}
		}};
	private static Activation[] asActiveConditions(Class<?>[] activeOnClasses,String[] activeOnClassNames) {
		activeOnClasses = firstNonNull(activeOnClasses, EMPTY_CLASS_ARRAY);
		activeOnClassNames = firstNonNull(activeOnClassNames, EMPTY_STRING_ARRAY);
		ArrayList<Activation> conditions = new ArrayList<>(activeOnClasses.length+activeOnClassNames.length);
		LinkedHashSet<Class<?>> classes = Sets.newLinkedHashSet(FluentIterable.from(activeOnClasses));
		FluentIterable.from(activeOnClassNames).transform(AS_CLASS).copyInto(classes);
		FluentIterable.from(classes)
			.filter(Predicates.and(Predicates.notNull(),Predicates.subtypeOf(Activation.class)))
			.transform(AS_ACTIVATION)
			.filter(Predicates.notNull()).copyInto(conditions);
		return conditions.toArray(new Activation[conditions.size()]);
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + Arrays.hashCode(conditions);
		result = prime * result + Objects.hash(and);
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ActivationConditions other = (ActivationConditions) obj;
		return and == other.and && Arrays.equals(conditions, other.conditions);
	}
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("ActivationConditions [conditions=").append(Arrays.toString(conditions)).append(", and=")
				.append(and).append("]");
		return builder.toString();
	}
}
